home *** CD-ROM | disk | FTP | other *** search
/ MacFormat España 13 / MacFormat n. 13 (Spain) / Macformat 13.bin / Shareware Internet / Newton / life-14 folder / life.nwt < prev    next >
Encoding:
Text File  |  1996-01-01  |  26.9 KB  |  932 lines

  1. Notes
  2. {labels: 'NIL, viewFont: 10241} // nil=Unfiled, or specify a folder, e.g., 'Business.  remove viewFont for current Styles default
  3. //ERASE! remove initial // to erase existing entries in this folder first
  4.  
  5. life.nwt -- Slurpee format
  6. 1/1/96
  7. Copyright 1994-96. S. Weyer.  All Rights Reserved Worldwide.
  8.  
  9. Newt source (verson 1.4) for "the game of Life"
  10. See life.txt & online help book for description 
  11.  
  12. (optional) life.bit for Bitmaps: LifeIcon
  13. (optional) LifeCnst.pkg plug-in for native methods ("arrN", "bitN")
  14. ----------
  15. init
  16. func()
  17. begin // several constants
  18.     :DefConst('kFirstRowCol,1000);
  19.     :DefConst('kLastRowCol,-1);
  20. end
  21. ----------
  22. Life
  23. //:doObj('add, 'Life)
  24. //:doObj('remove, 'Life)
  25. //:doObj('build, 'Life)
  26. {// the main Life application
  27. viewClass: clView,
  28. viewBounds: NIL,    // computed in viewSetupFormScript
  29. viewFlags: 4,       // vApplication
  30. viewFormat: 337,    // vfFillWhite + vfFrameBlack + vfPen*1,
  31. declareSelf: 'base, // for close box
  32. title: "Life 1.4",
  33.  
  34. // sizeX can be <= 30. others computed in viewSetupFormScript
  35. sizeX:     26, // initial width of universe. right bits of a longint to use [ <= 30]
  36.                // value of sizeX should also be included in xSizes list (see gridPicker)
  37.  
  38. // these are computed from sizeX at run-time in viewSetupFormScript
  39. sizeY:    NIL, // height of universe.
  40. cellSize: NIL, // size of a cell. maximize for screen width (maxAppWidth DIV sizeX)
  41. vSizeX:   NIL, // width of drawing view (cellSize*sizeX)  [<= maxAppWidth]
  42. vSizeY:   NIL, // height of drawing view
  43. offsetX:  NIL, // number of bit columns on left to skip (sizeX-32 *cellSize)
  44. maxAppWidth:   NIL,
  45. maxDrawHeight: NIL,
  46.  
  47. firstRow: NIL, // to avoid dead rows above
  48. lastRow:  NIL, // to avoid dead rows below
  49. firstCol: NIL, // ditto for columns
  50. lastCol:  NIL,
  51. pattern:  NIL, // defaults to dot. set by patternPicker
  52. nGen:      0,  // number of generations (clear =0, nextGeneration +1)
  53. universe: NIL, // representation of cells (array or bitmap)
  54. curMethod:  0, // arr*,arrI,arrN,bit,bitI,bitN.  set by methodPicker
  55.  
  56. kDrawTop: 20,  // extra space above drawArea (for title, gridPicker)
  57. kDrawBot: 35,  // below (for patternPicker, genPicker, status)
  58.  
  59. viewSetupFormScript: func()
  60.     begin
  61.         self.Life := self; // for run-time access (rather than :parent())
  62.         GetRoot().ExtrasDrawer:?close(); // get it out of the way. save a little heap
  63.         local ht, ap:= GetAppParams();   // configure to use entire current MP screen size
  64.         self.viewBounds := RelBounds(
  65.             ap.AppAreaLeft,ap.AppAreaTop,
  66.             maxAppWidth := ap.AppAreaWidth,
  67.             ht := ap.AppAreaHeight);
  68.         maxDrawHeight := ht - kDrawTop - kDrawBot;
  69.         stopped := true;
  70.         :setGrid(sizeX,nil);
  71.     end,
  72.     
  73. setGrid: func(x,redo) // intialize or resize drawing area
  74.     begin
  75.         universe := NIL;
  76.         sizeX    := x;
  77.         cellSize := maxAppWidth DIV sizeX;
  78.         sizeY    := maxDrawHeight DIV cellSize;
  79.         vSizeX   := cellSize * sizeX; // <= maxAppWidth. if sizeX divided evenly, no border
  80.         vSizeY   := cellSize * sizeY; // <= maxDrawHeight
  81.         offsetX  := (sizeX-32) * cellSize; // left bits not shown
  82.         :clear();
  83.         if redo then :RedoChildren();
  84.     end,
  85.  
  86. clear: func()
  87.     begin
  88.         :setUniverse(:makeUniverse());
  89.         nGen := 0;
  90.         firstRow := firstCol := kFirstRowCol; // all empty
  91.         lastRow  := lastCol  := kLastRowCol;
  92.         if drawArea exists
  93.         then drawArea:?Dirty(); // redraw next idle via viewDrawScript
  94.     end,
  95.  
  96. makeUniverse: func()
  97.     Array(sizeY, 0), // leave rows 0 until needed
  98.  
  99. setUniverse: func(u)
  100.     universe := u,
  101.  
  102. _package: {
  103.     icon: :GetPictAsBits("Bitmaps","LifeIcon"), // comment out if life.bit not transferred
  104.     },
  105. }
  106. ----------
  107. Life.init1~
  108. {// this gets added to app frame but a little easier for editing if it's separate
  109.  
  110. helpBook: :helpBookTemplate(), // copy data structure from Newt at development-time
  111. // actual help pages are added later, i.e., Life.helpBook+page1, etc.
  112.  
  113. helpView: NIL,
  114.  
  115. viewQuitScript: func()
  116.     begin
  117.     if helpView then helpView:close();
  118.     helpView := universe := NIL;
  119.     end,
  120.  
  121. // to support Rotate for Newton OS 2.0
  122. //reorienting: NIL,
  123. ReorientToScreen: func()
  124.     begin
  125.         //reorienting := TRUE;
  126.         :SyncView();
  127.         :RedoChildren();
  128.         //reorienting := NIL;
  129.     end,
  130. }
  131. ----------
  132. Life.idleStuff~
  133. {// some additional Life methods/slots related to idling ("Repeat")
  134. updateGen: TRUE, // to refresh gen counter during Repeat (default). set via genPicker
  135.  
  136. nCount: NIL, // num of gen for timing tests. e.g., 40.  set by genPicker
  137.  
  138. viewIdleScript: func()
  139.     if not stopped
  140.     then begin
  141.         if :nextGeneration() // generate
  142.         then begin
  143.             drawArea:drawDots(true);
  144.             if updateGen
  145.             then genPicker:updateText(NumberStr(nGen));
  146.             end;
  147.  
  148.         if firstRow < kFirstRowCol and (not nCount or nGen < nCount)
  149.         then delayMSecs // reschedule
  150.         else status.repeatButton:stop(nil); // empty universe. NIL
  151.         end,
  152.  
  153. delayMsecs: 5, // allow a few milliseconds for taps (e.g., Stop, Clear, Gen:)
  154. stopped: TRUE,
  155. }
  156. ----------
  157. Life.arr~
  158. {// these are added to app frame. also "copied" into _arr later
  159. // use an array for representation, draw dots explicitly
  160. //drawDots -- defined separately
  161.  
  162. getCell: func(row,x)
  163.     if row=0 // or x < 0 or x >= sizeX
  164.     then 0
  165.     else row[x],
  166.  
  167. getRow:    func(univ,y)
  168.     univ[y],
  169.  
  170. getUniverse: func()
  171.     universe,
  172.  
  173. // makeUniverse -- defined earlier
  174.  
  175. // nextGeneration -- defined separately
  176.  
  177. setCell: func(row,x,b)
  178.     begin
  179.         if row=0
  180.         then row := Array(sizeX, 0);
  181.         row[x] := if b then b else 1 - row[x];
  182.         row;
  183.     end,
  184.  
  185. setRow: func(univ,y,row)
  186.     univ[y] := row,
  187.  
  188. // setUniverse -- defined earlier
  189. }
  190. ----------
  191. Life.drawDots
  192. func(idle)
  193. if idle
  194. then :dirty() // let viewDrawScript do it
  195.  
  196. else if firstRow < kFirstRowCol
  197. then begin
  198.     local drawMode := '{ // for ovals
  199.         transferMode: 0, // modeCopy
  200.         fillPattern:  5, // vfFillBlack
  201.         };
  202.     local y,row,y1,y2,x,x1;
  203.     for y := firstRow to lastRow
  204.     do if isArray(row := universe[y]) // :getRow(universe,y)
  205.         then begin
  206.             y1 := y*cellSize; y2 := y1+cellSize; // constant for row
  207.             for x := firstCol to lastCol
  208.             do if row[x]>0 // :getCell(row,x)
  209.             then begin
  210.                 x1 := (sizeX-x-1)*cellSize;
  211.                 :DrawShape(MakeOval(x1,y1,x1+cellSize,y2),drawMode);
  212.                 end;
  213.             end;
  214.     end
  215. ----------
  216. Life.setCells
  217. func(row,x,vals,start,count) // generic version used by drawArea.viewGestureScript
  218. begin
  219.     local vx, vlen := 0, fill := 0;
  220.     if isArray(vals)
  221.     then vlen := Length(vals)
  222.     else fill := vals;
  223.     for vx := start to start+count-1
  224.     do row := :setCell(row,x-vx,if vx < vlen then vals[vx] else fill);
  225.     row;
  226. end
  227. ----------
  228. Life.nextGeneration
  229. func() // generic method (used by "arr" & "bit")
  230. if firstRow < kFirstRowCol // only bother if there are some cells
  231. then begin
  232.     local univ := :getUniverse();         // existing
  233.     local newuniv := :makeUniverse();    // new, empty universe
  234.     local x, y, neighbors, frx, lrx;
  235.  
  236.     local frow := firstRow-1, lrow := lastRow+1;
  237.     if frow<0 then frow := 0;
  238.     if lrow >= sizeY then lrow := sizeY-1;
  239.  
  240.     local fcol := firstCol-1, lcol := lastCol+1;
  241.     if fcol<0 then fcol := 0;
  242.     if lcol >= sizeX then lcol := sizeX-1;
  243.  
  244.     local newFirstRow := kFirstRowCol, newLastRow := kLastRowCol;
  245.     local newFirstCol := kFirstRowCol, newLastCol := kLastRowCol;
  246.  
  247.     local n1, n2, n3; // in this layout visually.
  248.     local n4, n5, n6;
  249.     local n7, n8, n9;
  250.     local row1 := 0 , row2 := 0, newRow := 0;
  251.     local row3 := :getRow(univ,frow);
  252.  
  253.     // process from top to bottom, from right to left
  254.     for y := frow to lrow // skip some dead rows before/after active area
  255.     do begin
  256.         row1 := row2; row2 := row3;
  257.         row3 := if y<lrow then :getRow(univ,y+1) else 0;
  258.         if row1<>0 or row2<>0 or row3<>0
  259.         then begin // this optimizes for empty row(s)
  260.             frx := kFirstRowCol; lrx := kLastRowCol;
  261.             n1 := :getCell(row1,fcol);
  262.             n4 := :getCell(row2,fcol);
  263.             n7 := :getCell(row3,fcol);
  264.             newRow := n2 := n5 := n8 := n3 := n6 := n9 := 0;
  265.             for x := fcol to lcol // skip some dead columns after/before active area
  266.             do begin
  267.                 if row1<>0
  268.                 then begin
  269.                     n3 := n2; n2 := n1;
  270.                     n1 := if x=lcol then 0 else :getCell(row1,x+1);
  271.                     neighbors := n1+n2+n3;
  272.                     end
  273.                 else neighbors := 0;
  274.                 if row2<>0
  275.                 then begin
  276.                     n6 := n5; n5 := n4;
  277.                     n4 := if x=lcol then 0 else :getCell(row2,x+1);
  278.                     neighbors := neighbors + n4+n6; // omit center
  279.                     end;
  280.                 if row3<>0
  281.                 then begin
  282.                     n9 := n8; n8 := n7;
  283.                     n7 := if x=lcol then 0 else :getCell(row3,x+1);
  284.                     neighbors := neighbors + n7+n8+n9;
  285.                     end;
  286.                 if neighbors=3 or (neighbors=2 and n5=1)
  287.                 then begin
  288.                     newRow := :setCell(newRow,x,1);
  289.                     if x < frx then frx := x; // 1st(min)
  290.                     lrx := x;
  291.                     end;
  292.                 end;
  293.             if lrx > kLastRowCol // something set in row?
  294.             then begin
  295.                 :setRow(newuniv,y,newRow);
  296.                 newLastRow := y; // last(max)
  297.                 if   y < newFirstRow then newFirstRow := y; // 1st(min)
  298.                 if frx < newFirstCol then newFirstCol := frx; // min
  299.                 if lrx > newLastCol  then newLastCol  := lrx; // max
  300.                 end;
  301.             end;
  302.         end;
  303.     firstRow := newFirstRow; lastRow := newLastRow;
  304.     firstCol := newFirstCol; lastCol := newLastCol;
  305.     :setUniverse(newuniv);
  306.     nGen := nGen+1; // <true>
  307.     // refresh done via viewIdleScript or Next
  308. end
  309. ----------
  310. Life+drawArea
  311. {// a drawing area (stepchild of Life)
  312. viewClass: clView,
  313. viewFlags:  2561, // vVisible + vClickable + vGesturesAllowed
  314. viewJustify:  16, // vjParentCenterH + vjParentTopV
  315. viewFormat:  337, // vfFillWhite + vfFrameBlack + vfPen*1,
  316.  
  317. viewSetupFormScript: func()
  318.     self.viewBounds := RelBounds(0,kDrawTop,vSizeX,vSizeY),
  319.  
  320. viewDrawScript: func()
  321.     begin // this is only invoked via :dirty (and it's stopped)
  322.         :drawDots(nil); // (re)defined by arr/bit
  323.         if stopped
  324.         then begin // draw grid
  325.             local x, y;
  326.             local drawMode := '{ // for lines
  327.                 penSize: 1,
  328.                 penPattern: 3, // vfGray
  329.                 };
  330.             for y := cellSize to vSizeY-cellSize by cellSize     // horizontal lines
  331.             do :DrawShape(MakeLine(0,y,vSizeX,y), drawMode);
  332.             for x := cellSize-1 to vSizeX-cellSize by cellSize     // vertical lines
  333.             do :DrawShape(MakeLine(x,0,x,vSizeY), drawMode);
  334.  
  335.             if updateGen and genPicker exists // might not during very initial build
  336.             then genPicker:updateText(NumberStr(nGen));
  337.             end;
  338.     end,
  339. }
  340. ----------
  341. Life.drawArea.viewGestureScript
  342. func(unit, gestureID)
  343. begin
  344.     local gb := :GlobalBox(), univ := :getUniverse();
  345.     local sb := StrokeBounds(unit);
  346.     local ylen := if pattern then Length(pattern) else 0;
  347.     // convert to local, grid coordinates. flip x
  348.     local maxX := sizeX-1, maxY := sizeY-1;
  349.     local x1 := maxX - max(0,    (sb.left   - gb.left) div cellSize);
  350.     local y1 :=        max(0,    (sb.top    - gb.top)  div cellSize);
  351.     local x2 := maxX - min(maxX, (sb.right  - gb.left) div cellSize);
  352.     local y2 :=        min(maxY, (sb.bottom - gb.top)  div cellSize);
  353.     local xlen := x1-x2+1, y, fill := 0;
  354.  
  355.     if gestureID=13    or                // aeScrub
  356.         (gestureID=16 and fill:=1)    // aeLine
  357.     then for y := y1 to y2    // fill area
  358.         do :setRow(univ,y, :setCells(:getRow(univ,y),x1, fill,0,xlen))
  359.  
  360.     else if ylen>0
  361.     then begin // copy a pattern
  362.         if y2+ylen > sizeY
  363.         then ylen := sizeY - y2; // clip pattern at bottom
  364.         y2 := y2 + ylen - 1;
  365.         xlen := Length(pattern[0]);  // note: uses length of first row as max width of pattern
  366.         if x2-xlen < -1
  367.         then xlen := x2+1; // clip pattern at right
  368.         x2 := x2 - xlen + 1;
  369.         for y := 0 to ylen-1
  370.         do :setRow(univ, y+y1, :setCells(:getRow(univ,y+y1),x1, pattern[y],0,xlen));
  371.         end
  372.  
  373.     else // toggle single cell
  374.         :setRow(univ,y1, :setCell(:getRow(univ,y1),x1,NIL));
  375.  
  376.     // expand live area
  377.     if y1 < firstRow then firstRow := y1;
  378.     if y2 > lastRow  then lastRow  := y2;
  379.     if x2 < firstCol then firstCol := x2;
  380.     if x1 > lastCol  then lastCol  := x1;
  381.  
  382.     :Dirty(); // returns true
  383. end
  384. ----------
  385. Life._arr
  386. {// this is the default version, so just copy the built-in versions
  387. drawDots:         Life.drawDots,
  388. getCell:         Life.getCell,
  389. getRow:            Life.getRow,
  390. getUniverse:    Life.getUniverse,
  391. makeUniverse:    Life.makeUniverse,
  392. nextGeneration:    Life.nextGeneration,
  393. setCell:        Life.setCell,
  394. setRow:            Life.setRow,
  395. setUniverse:    Life.setUniverse,
  396. }
  397. ----------
  398. Life._arrI
  399. {// same as _arr, but nextGeneration inlined
  400. drawDots:         _arr.drawDots,
  401. getCell:         _arr.getCell,
  402. getRow:            _arr.getRow,
  403. getUniverse:    _arr.getUniverse,
  404. makeUniverse:    _arr.makeUniverse,
  405. nextGeneration:    _arr.nextGeneration, // redefine (next)
  406. setCell:        _arr.setCell,
  407. setRow:            _arr.setRow,
  408. setUniverse:    _arr.setUniverse,
  409. }
  410. ----------
  411. Life._arrN
  412. {// same as _arr but nextGeneration native
  413. drawDots:         _arr.drawDots,
  414. getCell:         _arr.getCell,
  415. getRow:            _arr.getRow,
  416. getUniverse:    _arr.getUniverse,
  417. makeUniverse:    _arr.makeUniverse,
  418. nextGeneration: if arrN_nextGeneration exists then arrN_nextGeneration else _arr.nextGeneration,
  419. setCell:        _arr.setCell,
  420. setRow:            _arr.setRow,
  421. setUniverse:    _arr.setUniverse,
  422. }
  423. ----------
  424. Life._arrI.nextGeneration
  425. func() // inlined version. ("arrN" adds INT,ARRAY declarations)
  426. if firstRow < kFirstRowCol
  427. then begin
  428.     local univ := universe;
  429.     local newuniv := Array(sizeY,0);
  430.     local x, y, neighbors, frx, lrx;
  431.  
  432.     local frow := firstRow-1, lrow := lastRow+1;
  433.     if frow<0 then frow := 0;
  434.     if lrow >= sizeY then lrow := sizeY-1;
  435.  
  436.     local fcol := firstCol-1, lcol := lastCol+1;
  437.     if fcol<0 then fcol := 0;
  438.     if lcol >= sizeX then lcol := sizeX-1;
  439.  
  440.     local newFirstRow := kFirstRowCol, newLastRow := kLastRowCol;
  441.     local newFirstCol := kFirstRowCol, newLastCol := kLastRowCol;
  442.  
  443.     local n1, n2, n3;
  444.     local n4, n5, n6;
  445.     local n7, n8, n9;
  446.     local row1 := 0 , row2 := 0, newRow := 0;
  447.     local row3 := univ[frow];
  448.  
  449.     for y := frow to lrow
  450.     do begin
  451.         row1 := row2; row2 := row3;
  452.         row3 := if y<lrow then univ[y+1] else 0;
  453.         if row1<>0 or row2<>0 or row3<>0
  454.         then begin
  455.             frx := kFirstRowCol; lrx := kLastRowCol;
  456.             n1 := if row1=0 then 0 else row1[fcol];
  457.             n4 := if row2=0 then 0 else row2[fcol];
  458.             n7 := if row3=0 then 0 else row3[fcol];
  459.             newRow := n2 := n5 := n8 := n3 := n6 := n9 := 0;
  460.             for x := fcol to lcol
  461.             do begin
  462.                 if row1<>0
  463.                 then begin
  464.                     n3 := n2; n2 := n1;
  465.                     n1 := if x=lcol or row1=0 then 0 else row1[x+1];
  466.                     neighbors := n1+n2+n3;
  467.                     end
  468.                 else neighbors := 0;
  469.                 if row2<>0
  470.                 then begin
  471.                     n6 := n5; n5 := n4;
  472.                     n4 := if x=lcol or row2=0 then 0 else row2[x+1];
  473.                     neighbors := neighbors + n4+n6;
  474.                     end;
  475.                 if row3<>0
  476.                 then begin
  477.                     n9 := n8; n8 := n7;
  478.                     n7 := if x=lcol or row3=0 then 0 else row3[x+1];
  479.                     neighbors := neighbors + n7+n8+n9;
  480.                     end;
  481.                 if neighbors=3 or (neighbors=2 and n5=1)
  482.                 then begin
  483.                     if newRow=0
  484.                     then newRow := Array(sizeX, 0);
  485.                     newRow[x] := 1;
  486.                     if x < frx then frx := x;
  487.                     lrx := x;
  488.                     end;
  489.                 end;
  490.             if lrx > kLastRowCol
  491.             then begin
  492.                 newuniv[y] := newRow;
  493.                 newLastRow := y;
  494.                 if   y < newFirstRow then newFirstRow := y;
  495.                 if frx < newFirstCol then newFirstCol := frx;
  496.                 if lrx > newLastCol  then newLastCol  := lrx;
  497.                 end;
  498.             end;
  499.         end;
  500.     firstRow := newFirstRow; lastRow := newLastRow;
  501.     firstCol := newFirstCol; lastCol := newLastCol;
  502.     universe := newuniv;
  503.     nGen := nGen+1;
  504. end
  505. ----------
  506. Life._bit
  507. {// use a bitmap representation
  508. drawDots: func(idle) // this is sent to drawArea from viewIdleScript or viewDrawScript
  509.     :copyBits(universe, offsetX, 0, nil),
  510.  
  511. getCell: func(row,x) // return a bit: 0 or 1
  512.     // left shifting sign bit (x=29) fills 1's (so usually test first)
  513.     if row=0 //or x >= sizeX
  514.     then 0
  515.     else BAND(row >> x, 1),   // 0 <= x < sizeX. 0 on right
  516.  
  517. getRow: func(bits,y) // return a row (30-bit integer) in bitmap. 0<=y<sizeY
  518.     ExtractLong(bits, 16 + (y*4)),
  519.  
  520. getUniverse: func()  // return current bitmap
  521.     universe.bits,
  522.  
  523. makeUniverse: func()
  524.     begin // initialize a new bitmap
  525.         local myBits := SetLength(Clone("\u00000000000000000000000000000000"),
  526.                             16 + (4 * sizeY)); //rowBytes*pixelHeight
  527.         SetClass (myBits,'bits);
  528.         StuffWord(myBits,  4, 4);         // rowBytes
  529.         StuffWord(myBits, 12, sizeY);    // pixelHeight
  530.         StuffWord(myBits, 14, 32);        // pixelWidth
  531.         myBits; 
  532.     end,
  533.  
  534. nextGeneration: Life.nextGeneration, // generic version. same as _arr.nextGeneration
  535.  
  536. setCell: func(row,x,b) // set a bit to 0 or 1 (or NIL=toggle) & return new row
  537.     begin // [used only by viewClickScript currently]
  538.         local bit1 := 1 << x; // 0 <= x < sizeX. 0 on right
  539.         if row=0 and b<>0  // 1, NIL
  540.         then bit1
  541.         else if (if b then b=0 else BAND(row, bit1) <> 0)
  542.         then BAND(row, BNOT(bit1)) // :=0
  543.         else BOR(row, bit1);       // :=1
  544.     end,
  545.  
  546. setRow: func(bits,y,row) // replace a row (30-bit integer) in bitmap
  547.     StuffLong(bits, 16 + (y*4), row),
  548.  
  549. setUniverse: func(u) // set current bitmap
  550.     if isFrame(universe)
  551.     then universe.bits := u
  552.     else universe := { // a bitmap frame
  553.         bounds: RelBounds(0, 0, 32*cellSize, vSizeY),
  554.         bits:   u, // reset this each generation
  555.         },
  556. }
  557. ----------
  558. Life._bitI
  559. {// keep copies of refs
  560. drawDots:         _bit.drawDots,
  561. getCell:        _bit.getCell,
  562. getRow:            _bit.getRow,
  563. getUniverse:    _bit.getUniverse,
  564. makeUniverse:    _bit.makeUniverse,
  565. nextGeneration:    _bit.nextGeneration, // redefine (next)
  566. setCell:        _bit.setCell,
  567. setRow:            _bit.setRow,
  568. setUniverse:    _bit.setUniverse,
  569. }
  570. ----------
  571. Life._bitN
  572. {// same as bit but with two native methods from plug-in LifeConst:TKnollSys
  573. // nextGeneration and makeUniverse have some declarations and provide most of the benefit
  574. drawDots:         _bit.drawDots,
  575. getCell:        _bit.getCell,
  576. getRow:            _bit.getRow,
  577. getUniverse:    _bit.getUniverse,
  578. makeUniverse:    if bitN_makeUniverse exists then bitN_makeUniverse else _bit.makeUniverse,
  579. nextGeneration:    if bitN_nextGeneration exists then bitN_nextGeneration else _bit.nextGeneration,
  580. setCell:        _bit.setCell,
  581. setRow:            _bit.setRow,
  582. setUniverse:    _bit.setUniverse,
  583. }
  584. ----------
  585. Life._bitI.nextGeneration
  586. func() // inline version. ("bitN" adds INT declarations)
  587. if firstRow < kFirstRowCol
  588. then begin
  589.     local univ := universe.bits;
  590.     local newuniv := :makeUniverse();
  591.     // following locals are INT in native (bitN) version
  592.     local x, y, neighbors, frx, lrx;
  593.     local frow := firstRow-1, lrow := lastRow+1;
  594.     if frow<0 then frow := 0;
  595.     if lrow >= sizeY then lrow := sizeY-1;
  596.  
  597.     local fcol := firstCol-1, lcol := lastCol+1;
  598.     if fcol<0 then fcol := 0;
  599.     if lcol >= sizeX then lcol := sizeX-1;
  600.  
  601.     local newFirstRow := kFirstRowCol, newLastRow := kLastRowCol;
  602.     local newFirstCol := kFirstRowCol, newLastCol := kLastRowCol;
  603.  
  604.     local n1, n2, n3;
  605.     local n4, n5, n6;
  606.     local n7, n8, n9;
  607.     local row1 := 0 , row2 := 0, newRow := 0;
  608.     local row3 := ExtractLong(univ, 16 + (frow*4));
  609.  
  610.     for y := frow to lrow
  611.     do begin
  612.         row1 := row2; row2 := row3;
  613.         row3 := if y<lrow then ExtractLong(univ, 16 + (y+1)*4) else 0;
  614.         if row1<>0 or row2<>0 or row3<>0
  615.         then begin
  616.             frx := kFirstRowCol; lrx := kLastRowCol;
  617.             n1 := BAND(row1 >> fcol, 1);
  618.             n4 := BAND(row2 >> fcol, 1);
  619.             n7 := BAND(row3 >> fcol, 1);
  620.             newRow := n2 := n5 := n8 := n3 := n6 := n9 := 0;
  621.             for x := fcol to lcol
  622.             do begin
  623.                 if row1<>0
  624.                 then begin
  625.                     n3 := n2; n2 := n1;
  626.                     n1 := if x=lcol or row1=0 then 0 else BAND(row1 >> (x+1), 1);
  627.                     neighbors := n1+n2+n3;
  628.                     end
  629.                 else neighbors := 0;
  630.                 if row2<>0
  631.                 then begin
  632.                     n6 := n5; n5 := n4;
  633.                     n4 := if x=lcol then 0 else BAND(row2 >> (x+1), 1);
  634.                     neighbors := neighbors + n4+n6;
  635.                     end;
  636.                 if row3<>0
  637.                 then begin
  638.                     n9 := n8; n8 := n7;
  639.                     n7 := if x=lcol then 0 else BAND(row3 >> (x+1), 1);
  640.                     neighbors := neighbors + n7+n8+n9;
  641.                     end;
  642.                 if neighbors=3 or (neighbors=2 and n5=1)
  643.                 then begin
  644.                     newRow := BOR(newRow, 1 << x);
  645.                     if x < frx then frx := x;
  646.                     lrx := x;
  647.                     end;
  648.                 end;
  649.             if lrx > kLastRowCol
  650.             then begin
  651.                 StuffLong(newuniv, 16 + (y*4), newRow);
  652.                 newLastRow := y;
  653.                 if   y < newFirstRow then newFirstRow := y;
  654.                 if frx < newFirstCol then newFirstCol := frx;
  655.                 if lrx > newLastCol  then newLastCol  := lrx;
  656.                 end;
  657.             end;
  658.         end;
  659.     firstRow := newFirstRow; lastRow := newLastRow;
  660.     firstCol := newFirstCol; lastCol := newLastCol;
  661.     universe.bits := newuniv;
  662.     nGen := nGen+1;
  663. end
  664. ----------
  665. Life+titleObj
  666. {// title at top (displays Life.title)
  667. _proto: protoTitle,
  668. }
  669. ----------
  670. Life+gridPicker
  671. {// a picker for choosing size of grid
  672. _proto: protoLabelPicker,
  673. text: "grid",
  674. viewBounds: RelBounds(1,1,100,14),
  675. xSizes: [30,26,24,20,16,12], // some convenient sizes. max 30. include original sizeX
  676. labelActionScript: func(i)
  677.     Life:setGrid(xSizes[i],true),
  678. textSetup: func() // current size (sizeX had better be in list)
  679.     labelCommands[SetContains(xSizes,sizeX)],
  680. viewSetupFormScript: func()
  681.     begin
  682.     local xsize;
  683.     self.labelCommands :=
  684.         foreach xsize in xSizes // e.g., 30x35
  685.         collect xsize & "x" & maxDrawHeight DIV (maxAppWidth DIV xsize);
  686.     inherited:?viewSetupFormScript();
  687.     end,
  688. }
  689. ----------
  690. Life+methodPicker
  691. {// a picker for chooosing various representation/display methods
  692. _proto: protoLabelPicker,
  693. text: "method",
  694. viewBounds: RelBounds(-87,1,86,14),
  695. labelCommands: ["Arr", "ArrI", "ArrN", "Bit", "BitI", "BitN",],
  696. viewJustify: 32,    // vjParentRightH
  697. labelActionScript: func(i)
  698.     begin // splice in different versions of methods
  699.         curMethod := i;
  700.         local msg, meths := Life.(Intern("_" & labelCommands[i]));
  701.         foreach msg in '[
  702.             drawDots, getCell, getRow, getUniverse, makeUniverse,
  703.             nextGeneration, setCell, setRow, setUniverse]
  704.         do Life.(msg) := meths.(msg);
  705.         :clear();
  706.     end,
  707. textSetup: func()
  708.     labelCommands[curMethod],
  709. }
  710. ----------
  711. Life+patternPicker
  712. {// a picker for pre-built patterns
  713. _proto: protoLabelPicker,
  714. viewBounds: RelBounds(1, -33, 135, 14),
  715. text: "pattern",
  716. viewJustify: 128 + 8388608, // vjParentBottomV + vjLeftH + oneLineOnly,
  717. patterns: [// order matches names in labelCommands
  718.     NIL,         // none=dot
  719.     NIL,         // menu pickseparator line --------
  720.     [[0,1,0,],   // glider (note: 1st line of pattern should be max width (pad right with 0s if nec)
  721.      [1,],       //   subsequent rows can omit trailing 0s
  722.      [1,1,1,]],
  723.     [[1,1,1,]],  // blinker
  724.     [[1,1],      // block
  725.      [1,1]],
  726.     [[0,1,1,0,], // beehive
  727.      [1,0,0,1,],
  728.      [0,1,1,]],
  729.     ],
  730. labelCommands: ["<dot>", 'pickSeparator, "glider", "blinker", "block", "beehive",],
  731. labelActionScript: func(i)
  732.     pattern := patterns[i], // for copying in drawArea.viewGestureScript
  733. textSetup: func() // name of current pattern
  734.     labelCommands[SetContains(patterns,pattern)],
  735. }
  736. ----------
  737. Life+genPicker
  738. {// a "generation" count picker
  739. _proto: protoLabelPicker,
  740. text: "gen",
  741. viewBounds: RelBounds(-105,-33,104,14), // at right, just above status bar
  742. labelCommands: [
  743.     "0",             // update current gen# (nGen)
  744.     "<0>",            // don't update
  745.     "<time:40>",    // don't update. do timing&stop at 40
  746.     "<time:100>",    // don't udpate. do timing&stop at 100
  747.     ],
  748. viewJustify: 32+128,// vjParentRightH+vjParentBottomV
  749. labelActionScript: func(i)
  750.     begin
  751.         updateGen := i=0;
  752.         nCount :=
  753.             if i = 2
  754.             then 40
  755.             else if i=3
  756.             then 100
  757.             else NIL;
  758.         nGen := 0;
  759.     end,
  760. textSetup: func() // name of current gen option
  761.     labelCommands[
  762.         if ncount=40 then 2
  763.         else if ncount=100 then 3
  764.         else if updateGen then 0
  765.         else 1],
  766. }
  767. ----------
  768. ButtonBounds
  769. func(width)
  770. // used for adding embedded buttons to status
  771. // this is defined as a Newt development-time method
  772. // once the buttons are initialized and added to status,
  773. // it really isn't needed anymore (and it isn't saved with the package)
  774. // also not needed if ViewCnst.pkg plug-in is used
  775. begin
  776.     local left1:=25, top:=2, spacing:=6, bottom:=15;
  777.     if width > 0
  778.     then SetBounds(spacing,  top, spacing + width, bottom)   // other buttons
  779.     else SetBounds(left1,    top, left1 - width,   bottom);  // first button
  780. end
  781. ----------
  782. Life+status
  783. {// status bar with clock and closebox
  784. _proto: protoStatus,
  785. }
  786. ----------
  787. Life.status+clearButton
  788. {_proto: protoTextButton,
  789. viewBounds: :ButtonBounds(-40), // first button (stepchild) in status area
  790. text: "Clear",
  791. buttonClickScript: func()
  792.     Life:Delete('clear,[]), // built-in crumple/trash can effect
  793. }
  794. ----------
  795. Life.status+nextButton
  796. {_proto: protoTextButton,
  797. viewBounds: :ButtonBounds(40), // second button
  798. viewJustify: 8389638, // vjParentLeftH + vjParentTopV + vjSiblingRightH + oneLineOnly + vjCenterH + vjCenterV,
  799. text: "Next",
  800. buttonClickScript: func()
  801.     if stopped
  802.     then begin
  803.         :nextGeneration();
  804.         drawArea:dirty();
  805.     end,
  806. }
  807. ----------
  808. Life.status+repeatButton
  809. {_proto: protoTextButton,
  810. viewBounds: :ButtonBounds(40), // third button
  811. viewJustify: 8389638,
  812. text: "Repeat",
  813.  
  814. buttonClickScript: func()
  815.     if firstRow < kFirstRowCol // not empty?
  816.     then if stopped 
  817.         then begin // start up background process
  818.             gc(); time1 := Ticks();
  819.             stopped := Life:setupIdle(delayMsecs); // start background process. NIL
  820.             SetValue(self, 'text, "Stop");
  821.             end
  822.         else :stop(true),
  823.  
  824. stop: func(fl)
  825.     begin // used to stop things from repeatButton.buttonClickScript or viewIdleScript
  826.         if fl
  827.         then Life:SetupIdle(0);  // halt background process
  828.         if nCount
  829.         then begin
  830.             Print("time:" && Ticks() - time1);
  831.             nGen := 0;
  832.             end;
  833.            stopped := TRUE;
  834.         SetValue(self, 'text, "Repeat");
  835.         drawArea:dirty(); // redraw with grid
  836.         NIL; // turn off idling by returning nil
  837.     end,
  838. time1: NIL, // for possible timing (if nCount non-nil)
  839. }
  840. ----------
  841. Life.status+zhelpButton
  842. {_proto: protoTextButton,
  843. viewBounds: :ButtonBounds(40), // fourth button (zhelp sorts 4th)
  844. viewJustify: 8389638,
  845. text: "Info",
  846. buttonClickScript: func()
  847.     if helpBook and stopped // open help book
  848.     then begin
  849.         local TT := GetRoot().TinyTim; // the help reader app
  850.         if not helpView
  851.         then helpView := BuildContext(
  852.             {_proto: TT._proto,
  853.             bookRef: helpBook});
  854.         TT:Close();   // in case system help is currently open
  855.         helpView:openManual(helpBook);
  856.         end,
  857. }
  858. ----------
  859. Life.helpBook+page1
  860. .# first help book page.  this is much easier than dealing with BookMaker!
  861. .subject 1
  862. Describe Life
  863. .story
  864. Life is a mathematical simulation game described by John Conway in Scientific American in Oct. 1970.
  865.  
  866. Several rules are used to create a next generation from a cell's 8 neighbors:
  867. - Survival: A cell with 2 or 3 neighbors survives.
  868. - Death: A cell with 1 or less, or 4 or more neighbors, "dies".
  869. - Birth: An empty cell with exactly 3 neighbors will be "born".
  870. ----------
  871. Life.helpBook+page2
  872. .subject 1
  873. Understand the Code
  874. .story
  875. This version of Life (1.4) has six variations/optimizations, which are explained in the file: life.txt.
  876.  
  877. The article "Life with NewtonScript" by David Betz in Byte, March 1994, pp. 191-194 provides some introduction/background.
  878.  
  879. You could also register for the Newt Development Environment (see Develop) to obtain a Newton programming manual and more NewtonScript application examples.
  880. ----------
  881. Life.helpBook+page3
  882. .subject 1
  883. Distribute
  884. .story
  885. This version of Life is freeware and may be distributed freely as long as all of the files are included and unmodified.  You are free to make modifications for your own use.
  886.  
  887. Copyright 1994-96, S. Weyer. All Rights Reserved Worldwide.
  888. ----------
  889. Life.helpBook+page4
  890. .subject 1
  891. Use
  892. .story
  893. \uFC01\u grid: grid size
  894. \uFC01\u method: computation method
  895. \uFC01\u pattern: fill pattern for <tap>
  896. \uFC01\u gen: turn on/off counter,timing
  897.  
  898. <tap>: add a pattern, or toggle a cell
  899. <line>: fill an area
  900. <scrub>: erase an area
  901.  
  902. [Clear]: clear all cells, reset Gen. counter
  903. [Next]: evolve a single generation
  904. [Repeat]: evolve until all cells are empty or you tap Stop or Clear
  905. [Info]: this help book
  906. ----------
  907. Life.helpBook+page5
  908. .subject 1
  909. Develop
  910. .story
  911. If you are interested in object-oriented programming, developing applications in NewtonScript and saving as packages directly on your Newton, try the "Newt" development environment.
  912.  
  913. Registered users receive a 80+ pp. manual (Acrobat or paper) and 190+ examples.
  914.  
  915. For further info, see life.txt or http://www.netaxs.com/~weyer/newton/releases.html 
  916. ----------
  917. Life.helpBook+page6
  918. .subject 1
  919. Contact Author
  920. .story
  921. Steve Weyer
  922. 17 Timber Knoll Drive
  923. Washington Crossing, PA 18977-1052
  924. Internet: weyer@netaxs.com
  925. America Online, eWorld, NewtonMail: SteveWeyer
  926. Compuserve: 74603,2051
  927.  
  928. http://www.netaxs.com/~weyer/newton/releases.html
  929. ----------
  930. BYE!
  931.         
  932.